#pragma once
#ifndef _LINAL_CSRMATRIX_
#define _LINAL_CSRMATRIX_

//================================================================================================================================
//================================================================================================================================
// QtCore
#include <QtCore/QVector>
// Локальные
#include "math/math.h"
#include "linal/vector.h"
#include "core_global.h"

//================================================================================================================================
//================================================================================================================================
namespace linal
{
    //! Матрица в разреженно-строчном формате.
    //!	\sa Метод конечных элементов для решения скалярных и векторных задач : учеб. пособие / Ю.Г. Соловейчик, М.Э. Рояк,
    //! М.Г. Персова - Новосибирск: Изд-во НГТУ, 2007. - 896 с.
    class CORE_EXPORT CSRMatrix
    {
    public:
        //! Создание матрицы нулевой размерности.
        CSRMatrix() : m_Dimension(0), m_ig(0), m_jg(0), m_ig2(0), m_jg2(0), m_di(0), m_ggl(0), m_ggu(0) {}

        //! Создание нулевой матрицы размерности \e dimension с заданным портретом.
        //! \param dimension Размерность матрицы.
        //! \param ig Информация о количестве ненулевых элементов в каждой строке матрицы.
        //! \param jg Информация о номерах столбцов ненулевых элементов матрицы.
        CSRMatrix(int dimension, const QVector<int> &ig, const QVector<int> &jg, const QVector<int> &ig2, const QVector<int> &jg2) :
            m_Dimension(dimension), m_ig(ig), m_jg(jg), m_ig2(ig2), m_jg2(jg2), m_di(dimension, 0.), m_ggl(ig.last(), 0.), m_ggu(ig.last(), 0.) {}

        //! Изменить размерность и портрет матрицы.
        //! \remarks Данная опреция обнуляет матрицу (присваивает всем элементам матрицы нулевое значение).
        //! \param dimension Размерность матрицы.
        //! \param ig Информация о количестве ненулевых элементов в каждой строке матрицы.
        //! \param jg Информация о номерах столбцов ненулевых элементов матрицы.
        void setMatrixPortrait(int dimension, const QVector<int> &ig, const QVector<int> &jg, const QVector<int> &ig2, const QVector<int> &jg2)
        {
            m_Dimension = dimension;
            m_ig = ig;
            m_jg = jg;
            m_ig2 = ig2;
            m_jg2 = jg2;
            m_di.fill(0., dimension);
            m_ggl.fill(0., ig.last());
            m_ggu.fill(0., ig.last());

        }

        //! Обнулить все элементы матрицы (присвоить им нулевое значение).
        //! \remarks Функция создана для обнуления матрицы перед началом сборки СЛАУ для МКЭ.
        void zeroize() {m_di.fill(0.); m_ggl.fill(0.); m_ggu.fill(0.);}

        //! Сделать матрицу единичной.
        //
        //! Все элементы диагонали приравниваются к единице, а верхний и нижний треугольники обнуляются.
        //! Портрет матрицы сохраняется.
        void makeIdentity() {m_di.fill(1.); m_ggl.fill(0.), m_ggu.fill(0.);}

        //! Указанную строку матрицы превратить в строку единичной матрицы.
        //
        //! Диагональный элемент указанной строки становится равным единице, а все остальные элементы строки зануляются.
        //! При этом портрет матрицы не изменяется.
        void makeIdentity(int row);

        //! Получить размерность матрицы.
        int dimension() const {return m_Dimension;}

        //! Транспонировать матрицу.
        void transpose() {math::permute(m_ggl, m_ggu);}

        //! Получить матрицу, полученную из данной с помощью транспонирования.
        CSRMatrix transposed() const
        {
            CSRMatrix result(m_Dimension, m_ig, m_jg, m_ig2, m_jg2);
            result.m_di = m_di;
            result.m_ggl = m_ggl;
            result.m_ggu = m_ggu;
            return result;
        }

        //! Получение информацию о количестве ненулевых элементов в каждой строке матрицы.
        //! \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        //! внутренней структуре матрицы.
        const QVector<int>& ig() const {return m_ig;}

        //! Получение информацию о номерах столбцов ненулевых элементов матрицы.
        //! \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        //! внутренней структуре матрицы.
        const QVector<int>& jg() const {return m_jg;}

        //! Получение данных (в формате CSR) о диагонали матрицы.
        //! \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        //! внутренней структуре матрицы.
        const QVector<double>& di() const {return m_di;}

        //! Получение данных (в формате CSR) о нижнем треугольнике матрицы.
        //! \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        //! внутренней структуре матрицы.
        const QVector<double>& ggl() const {return m_ggl;}

        //! Получение данных (в формате CSR) о верхнем треугольнике матрицы.
        //! \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        //! внутренней структуре матрицы.
        const QVector<double>& ggu() const {return m_ggu;}

        //! Неполная LU факторизация матрицы.
        //! \sa Метод конечных элементов для решения скалярных и векторных задач : учеб. пособие / Ю.Г. Соловейчик, М.Э. Рояк,
        //! М.Г. Персова - Новосибирск: Изд-во НГТУ, 2007. - 896 с.
        //! \return Матрица в разреженно-строчном формате. Нижний треугольник и диагональ матрицы дают матрицу факторизации L, а
        //! верхний треугольник - матрицу факторизации U.
        CSRMatrix partFactorLU() const;

        //! Получение доступа к элементу матрицы A[i,j].
        //! \remarks Синтаксис нестандартный, но это позволяет сделать доступ к элементу матрицы максимально быстрым и логически ясным.
        double& operator()(int i, int j);

        //! Перегруженная функция.
        const double& operator()(int i, int j) const;

    private:
        int m_Dimension;	//!< Размерность матрицы.
        QVector<int> m_ig;	//!< Информация о количестве ненулевых элементов в каждой строке матрицы.
        QVector<int> m_jg;	//!< Информация о номерах столбцов ненулевых элементов матрицы.
        QVector<int> m_ig2;
        QVector<int> m_jg2;
        QVector<double> m_di;	//!< Диагональ матрицы.
        QVector<double> m_ggl;	//!< Нижний треугольник матрицы.
        QVector<double> m_ggu;	//!< Верхний треугольник матрицы.
    };

    //============================================================================================================================
    //============================================================================================================================
    //! Умножение матрицы на вектор.
    //! \relates Vector
    //! \relates CSRMatrix
    CORE_EXPORT const Vector operator*(const CSRMatrix &matrix, const Vector &vector);
}

#endif // _LINAL_CSRMATRIX_
